/////////////////////////////////////////////////////////////////////////////
// Original code from libhdfs3. Copyright (c) 2013 - 2014, Pivotal Inc.
// All rights reserved. Author: Zhanwei Wang
/////////////////////////////////////////////////////////////////////////////
//  Modifications by Kumo Inc.
// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//



#ifndef _HDFS_LIBHDFS3_RPC_RPCREMOTECALL_
#define _HDFS_LIBHDFS3_RPC_RPCREMOTECALL_

#include <turbo/times/time.h>
#include <kmhdfs/common/exception_internal.h>
#include <memory>
#include <kmhdfs/rpc/rpc_call.h>
#include <kmhdfs/rpc/rpc_protocol_info.h>
#include <kmhdfs/common/thread.h>
#include <kmhdfs/common/write_buffer.h>

#define INVALID_RETRY_COUNT -1

namespace Hdfs {
namespace Internal {

class RpcRemoteCall;
typedef std::shared_ptr<RpcRemoteCall> RpcRemoteCallPtr;

class RpcRemoteCall {
public:
    RpcRemoteCall(const RpcCall & c, int32_t id, const std::string & clientId) :
        complete(false), identity(id), call(c), clientId(clientId) {
    }
    virtual ~RpcRemoteCall() {
    }

    virtual void cancel(exception_ptr reason) {
        unique_lock<mutex> lock(mut);
        complete = true;
        error = reason;
        cond.notify_all();
    }

    virtual void serialize(const RpcProtocolInfo & protocol,
                           WriteBuffer & buffer);

    const int32_t getIdentity() const {
        return identity;
    }

    void wait() {
        unique_lock<mutex> lock(mut);

        if (!complete) {
            cond.wait_for(lock, std::chrono::milliseconds(500));
        }
    }

    void check() {
        if (error != exception_ptr()) {
            rethrow_exception(error);
        }
    }

    RpcCall & getCall() {
        return call;
    }

    void done() {
        unique_lock<mutex> lock(mut);
        complete = true;
        cond.notify_all();
    }

    void wakeup() {
        cond.notify_all();
    }

    bool finished() {
        unique_lock<mutex> lock(mut);
        return complete;
    }

public:
    static std::vector<char> GetPingRequest(const std::string & clientid);

private:
    bool complete;
    condition_variable cond;
    const int32_t identity;
    exception_ptr error;
    mutex mut;
    RpcCall call;
    std::string clientId;
};

}
}

#endif /* _HDFS_LIBHDFS3_RPC_RPCREMOTECALL_ */
